#pragma once
#include <sstream>
#include "httplib.h" 
// 这里的httplib.h并非原httplib.h，我将5529行和772行的内容注释掉了
// 并且由于命名的冲突，我将log的命名空间和log.h里的内容进行了修改
#include "data_manage.hpp"
#include "util.hpp"

extern cloud::data_manage *dm;

namespace cloud
{
    class http_server
    {
    public:
        http_server(){}
        ~http_server(){}

        void init()
        {
            config *c = config::get_instance();
            _server_ip = c->get_server_ip();
            _server_port = c->get_server_port();
            _download_prefix = c->get_download_prefix();
        }

        void start()
        {
            _server.Post("/upload", upload);
            _server.Get("/", list);
            _server.Get("/list", list);

            //(.*)为正则表达式，.表示字符，*表示任意多个
            std::string download_str = _download_prefix + "(.*)";
            _server.Get(download_str.c_str(), download);
            
            _server.listen("0.0.0.0", 8888);
        }

    private:
        static void upload(const httplib::Request &req, httplib::Response &res)
        {
            // post /upload
            // 文件数据在正文中，但正文并不全是文件数据
            auto ret = req.has_file("file");
            if(ret == false)
            {
                res.status = 400;
                //INFO("请求的file字段不存在");
                std::cout << "请求的file字段不存在" << std::endl;
                return;
            }

            auto file = req.get_file_value("file");
            // file.filename为文件名称
            // file.content为文件内容
            std::string back_dir = config::get_instance()->get_back_dir();
            std::string real_path = back_dir + file_util(file.filename).file_name();
            file_util fu(real_path);
            fu.set_content(file.content);
            backup_info info(real_path);
            dm->insert(info);
            //INFO("有文件上传，已接收之 --> %s", fu.file_name().c_str());
            std::cout << "有文件上传，已接收之" << std::endl;
            res.status = 200;
        }

        // 展示文件信息列表
        static void list(const httplib::Request &req, httplib::Response &res)
        {
            //INFO("list");
            // 获取所有文件的信息
            std::vector<backup_info> files_info;
            dm->get_all(files_info);
            //INFO("list1");

            std::stringstream ss;
            ss << R"(<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>云备份</title></head><body><h1 style="font-size: 40px;">文件下载</h1>)";
            for(auto &i : files_info)
            {
                std::string file_name = file_util(i._real_path).file_name();
                ss << "<p><a href='" << i._url << "'>" << file_name << "</a> ";
                ss << i._fsize/1024  << "Kb ";
                ss << util::t_to_s(i._mtime) << "</p>";
            }
            ss << "</body></html>";

            //INFO("ss:%s", ss.str().c_str());

            res.body = ss.str();
            res.set_header("Content-Type", "text/html");
            res.status = 200;
        }

        static void download(const httplib::Request &req, httplib::Response &res)
        {
            // 断点续传
            // http请求
            // GET /download/test.txt HTTP/1.1
            // If-Range:"服务端下载时响应的Etag字段" 用于服务端判断这个文件是否与原先下载的文件一致
            // Range:bytes 100-10000

            // http响应
            // HTTP/1.1 206 Partial Content
            // Etag:"file_name-fsize-mtime" 用于标识文件
            // Content-Range:bytes 100-10000/文件大小
            // 有效载荷：对应区间数据

            // 根据资源路径，获取文件信息
            backup_info info;
            dm->get_one_by_url(req.path, info);
            // 判断文件是否被压缩
            if(info._pack_flag == true)
            {
                INFO("文件已被压缩，解压之 %s --> %s", info._pack_path.c_str(), info._real_path.c_str());
                // 解压到备份目录下
                file_util fu(info._pack_path);
                // fu.decompress(info._real_path);
                // 删除压缩包，修改并更新文件信息
                fu.file_remove();
                info._pack_flag = false;
                dm->updata(info);
            }

            // 进行断点续传判断
            bool resume_flag = false;//断点续传标志
            std::string old_etag;
            if(req.has_header("If-Range"))
            {
                old_etag = req.get_header_value("If-Range");
                if(old_etag == get_etag(info))
                {
                    INFO("触发断点续传 %s", info._real_path.c_str());
                    resume_flag = true;
                }
            }

            // 读取文件数据
            file_util fu(info._real_path);
            fu.get_content(res.body);
            if(resume_flag == true)
            {
                // httplib.h中第6374行实现了对断点续传请求的处理
                // 只须将数据读取到res.body中即可
                res.set_header("Accept-Ranges", "bytes");
                res.set_header("Etag", get_etag(info));
                res.set_header("Content-Type", "application/octet-stream");
                res.status = 206;//区间请求，状态码206

                // string range = req.get_header_val("Range");  bytes=start-end
                // res.set_header("Content-Range", "bytes");
            }
            else
            {
                // 设置响应的头部字段
                res.set_header("Accept-Ranges", "bytes");
                res.set_header("Etag", get_etag(info));
                // Content-Type字段决定了浏览器如何处理这个响应
                res.set_header("Content-Type", "application/octet-stream");
                res.status = 200;
            }
        }

        static std::string get_etag(const backup_info &info)
        {
            // etag: file_name-fsize-mtime
            file_util fu(info._real_path);
            std::stringstream ss;
            ss << fu.file_name();
            ss << "-";
            ss << info._fsize;
            ss << "-";
            ss << info._mtime;
            return ss.str();
        }

    private:
        std::string _server_ip;
        uint16_t _server_port;
        std::string _download_prefix;
        httplib::Server _server;
    };
}